﻿using System;
using System.Collections.Specialized;
using System.Text;
using System.Web;
using System.IO;
using System.Web.Mvc;
using System.Web.Routing;

namespace CoolCode.Web.Mvc.UI
{
	/// <summary>
	/// Renders a pager component from an IPagination datasource.
	/// </summary>
	public class Pager : IPageable
	{
		private readonly IPageable _source;
		private readonly HttpRequestBase _request;
		private readonly TextWriter _output;
		private readonly RequestContext _requestContext;
		private readonly RouteValueDictionary _routeValues;

		private string _controllerName;
		private string _actionName;

		protected  string _css = "pager";
		/*页次：<b>{PageIndex}</b>/<b>{PageCount}</b>页 每页记录：<b>{PageSize}</b>条 记录总数：<b>{TotalRecords}</b>*/
		protected string _summaryFormat = "<span>Page <b>{PageIndex}</b>/<b>{PageCount}</b>, Showing {From} - {To} of {TotalRecords} </span>";
		protected string _firstPageText = "<<";
		protected string _previousPageText = "<";
		protected string _nextPageText = ">";
		protected string _lastPageText = ">>";
		protected string _pageQueryName = "page";
		protected string _position = "left";
		protected bool _numericPageVisible = true;
		protected bool _summaryInfoVisible = true;
		protected bool _inputVisible = true;
		protected string _onclick = null;
		protected Func<int, string> _urlBuilder;

		/// <summary>
		/// Creates a new instance of the Pager class.
		/// </summary>
		/// <param name="source">The datasource</param>
		/// <param name="context">view context</param>
		public Pager(IPageable source, HttpContextBase httpContext, RequestContext requestContext, RouteValueDictionary routeValues)
		{
			_source = source;
			_request = httpContext.Request;
			_output = new StringWriter(); // httpContext.Response.Output;
			_requestContext = requestContext;
			_routeValues = routeValues;
			_controllerName = (string)_routeValues["controller"];
			_actionName = (string)_routeValues["action"];

			_urlBuilder = CreateDefaultUrl;
			PageIndex = source.PageIndex;
			PageSize = source.PageSize;
			TotalRecords = source.TotalRecords;
		}

		/// <summary>
		/// Specifies the query string parameter to use when generating pager links. The default is 'page'
		/// </summary>
		public Pager QueryParam(string queryStringParam)
		{
			_pageQueryName = queryStringParam;
			return this;
		}

		/// <summary>
		/// Specifies the format to use when rendering a pagination containing multiple pages. 
		/// The default is 'Page {PageIndex}/{PageCount}, Showing {From} - {To} of {TotalRecords}'  
		/// </summary>
		public Pager SummaryFormat(string format)
		{
			_summaryFormat = format;
			return this;
		}

		public Pager SummaryFormat(Func<string> format)
		{
			_summaryFormat = format();
			return this;
		}

		/// <summary>
		/// Text for the 'first' link.
		/// </summary>
		public Pager First(string first)
		{
			_firstPageText = first;
			return this;
		}

		/// <summary>
		/// Text for the 'prev' link
		/// </summary>
		public Pager Previous(string previous)
		{
			_previousPageText = previous;
			return this;
		}

		/// <summary>
		/// Text for the 'next' link
		/// </summary>
		public Pager Next(string next)
		{
			_nextPageText = next;
			return this;
		}

		/// <summary>
		/// Text for the 'last' link
		/// </summary>
		public Pager Last(string last)
		{
			_lastPageText = last;
			return this;
		}

		/// <summary>
		/// Uses a lambda expression to generate the URL for the page links.
		/// </summary>
		/// <param name="urlBuilder">Lambda expression for generating the URL used in the page links</param>
		public Pager Link(Func<int, string> urlBuilder)
		{
			_urlBuilder = urlBuilder;
			return this;
		}

		public Pager Align(string position)
		{
			_position = position;
			return this;
		}

		public Pager Css(string css)
		{
			_css = css;
			return this;
		}

		public Pager NumericPageVisible(bool visible)
		{
			_numericPageVisible = visible;
			return this;
		}

		public Pager SummaryInfoVisible(bool visible)
		{
			_summaryInfoVisible = visible;
			return this;
		}

		public Pager InputVisible(bool visible)
		{
			_inputVisible = visible;
			return this;
		}

		public Pager Url(Func<int, string> builder)
		{
			_urlBuilder = builder;
			return this;
		}

		public Pager OnClick(string script)
		{
			_onclick = script;
			return this;
		}

		public override string ToString()
		{
			Render(_output);
			return _output.ToString();
		}

		#region Render

		public virtual void Render(TextWriter output)
		{
			output.Write("<div class=\"{0}\" align=\"{1}\">", _css, _position);
			if (this.TotalRecords > 0)
			{
				RenderPageLink(output, 0, _firstPageText, !HasPreviousPage);
				RenderPageLink(output, PageIndex - 1, _previousPageText, !HasPreviousPage);

				//Render numeric page
				if (_numericPageVisible)
				{
					RenderNumericPages(output);
				}

				RenderPageLink(output, PageIndex + 1, _nextPageText, !HasNextPage);
				RenderPageLink(output, PageCount - 1, _lastPageText, !HasNextPage);
			}

			if (_inputVisible)
			{
				//TODO:
				// RenderInput(output);
			}

			if (_summaryInfoVisible)
			{
				RenderSummaryInfo(output);
			}
			output.Write("</div>"); 
		}

		protected virtual void RenderSummaryInfo(TextWriter output)
		{
			string summaryHtml = _summaryFormat
				.Replace("{PageIndex}", (this.PageIndex + 1).ToString())
				.Replace("{PageCount}", this.PageCount.ToString())
				.Replace("{PageSize}", this.PageSize.ToString())
				.Replace("{TotalRecords}", this.TotalRecords.ToString())
				.Replace("{From}", (this.PageSize * this.PageIndex + 1).ToString())
				.Replace("{To}", ((this.PageIndex == this.PageCount - 1) ? this.TotalRecords : (this.PageSize * (this.PageIndex + 1))).ToString());
			
			output.Write(summaryHtml);
		}

		protected virtual void RenderPageLink(TextWriter output, int pageIndex, string displayText, bool disabled)
		{
			if (!disabled)
			{
				if (string.IsNullOrEmpty(_onclick))
				{
					output.Write("<a class=\"pagerLink\" href=\"{0}\">{1}</a>", _urlBuilder(pageIndex), displayText);
				}
				else
				{
					output.Write("<a class=\"pagerLink\" href=\"{0}\" onclick=\"{2}\">{1}</a>", _urlBuilder(pageIndex), displayText, _onclick);
				}
			}
			//else
			//{
			//    output.Write("<span class=\"disabled\">{0}</span>", displayText);
			//}
		}

		protected virtual void RenderPageLink(TextWriter output, int pageIndex, bool isCurrentPage)
		{
			string displayText = (pageIndex + 1).ToString();
			if (!isCurrentPage)
			{ 
				RenderPageLink(output, pageIndex, displayText, false);
			}
			else
			{
				output.Write("<span class=\"current\">{0}</span>", displayText);
			}
		}

		protected virtual void RenderNumericPages(TextWriter output)
		{
			const int MiddlePageIndex = 5;
			const int PageRange = MiddlePageIndex * 2;
			Func<int, int> clipIfMore = value =>
			{
				if (value > this.PageCount - 1)
				{
					return this.PageCount - 1;
				}
				return value;
			};

			Func<int, int> clipIfLess = value =>
			{
				if (value < 0)
				{
					return 0;
				}
				return value;
			};

			int maxpage = clipIfMore(this.PageIndex + MiddlePageIndex);
			int minpage = clipIfLess(this.PageIndex - MiddlePageIndex + 1);
			if (maxpage - minpage + 1 < PageRange)
			{
				if (minpage > 0)
				{
					minpage = clipIfLess(maxpage - PageRange + 1);
				}
				if (maxpage < this.PageCount - 1)
				{
					maxpage = clipIfMore(minpage + PageRange - 1);
				}
			}

			if (minpage > 0)
			{
				RenderSuspensionPoints(output);
			}
			for (int i = minpage; i <= maxpage; i++)
			{
				bool isCurrentPage = (i == this.PageIndex);
				RenderPageLink(output, i, isCurrentPage);
			}
			if (maxpage < this.PageCount - 1)
			{
				RenderSuspensionPoints(output);
			}
		}

		protected virtual void RenderSuspensionPoints(TextWriter output)
		{
			output.Write("...");
		}

		#endregion

		protected virtual string CreateDefaultUrl(int pageIndex)
		{
			var queryString = _request.QueryString;
			var dict = new RouteValueDictionary(_routeValues);

			//保留查询字符到下一页
			foreach (string key in queryString.Keys)
				dict[key] = queryString[key];

			dict[_pageQueryName] = pageIndex;
			UrlHelper urlHelper = new UrlHelper(_requestContext);
			string url = urlHelper.Action(_actionName, dict);
			return url;
		}


		#region IPageable Members

		public bool HasPreviousPage
		{
			get
			{
				return (PageIndex > 0);
			}
		}

		public bool HasNextPage
		{
			get
			{
				return (PageIndex + 1 < PageCount);
			}
		}

		public int PageCount
		{
			get
			{
				return (int)Math.Ceiling(TotalRecords / (double)PageSize);
			}
		}

		public int PageIndex
		{
			get;
			private set;
		}

		public int PageSize
		{
			get;
			private set;
		}

		public int TotalRecords
		{
			get;
			private set;
		}

		#endregion
	}
}
